home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 432_01 / ptmid3 / ptmid.c < prev    next >
C/C++ Source or Header  |  1994-07-17  |  15KB  |  526 lines

  1. /*
  2.  * ptmid.c: Creates Protracker MODule files from MIDI files.
  3.  * (My first attempt at Hungarian Notation.. wince!)
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1 - first release
  9.  *       11/2/1994  ver 0.2 - added stats + fixed 15 handles limit
  10.  *       29/6/1994  ver 0.3 - second release
  11.  */
  12.  
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include <io.h>
  18. #include <stdarg.h>
  19. #include <math.h>
  20. #include "ptmid.h"
  21. #include "samples.h"
  22.  
  23. #ifndef R_OK
  24. #define R_OK 4
  25. #endif
  26.  
  27. #if __MSDOS__
  28. #define SEPARATOR '\\'
  29. #else
  30. #define SEPARATOR '/'
  31. #endif
  32.  
  33. /** Define HACKED_VERSION only if compiled under my Turbo C 2.0 variant **/
  34. #define HACKED_VERSION
  35.  
  36. #define WTSP " \t\n"
  37.  
  38. char bDrumch = 9, szId[5];
  39. int fNocopy = 0, fQuiet = 0, fExtend = 0, fStats = 0;
  40. int wRgmode = 0, wPatmax = 128, wMaxchan = 4, wQuantval = 16, wModfmt = 1;
  41. SI *rgpsiDrum[128], **rgppsiIns[129], *psiTree = NULL;
  42. Sz szTitle = "Converted by PTMID!", szQuant = NULL, szProgram;
  43. Fn fnSampath = {0};
  44.  
  45. /*
  46.  * Init: Does all those initialization things (which aren't too involved).
  47.  */
  48. static void Init(void)
  49. {
  50.     int i;
  51.  
  52. #ifdef HACKED_VERSION
  53.   void increase_handles(void);
  54.  
  55.     increase_handles(); /** Increase possible file handles to 40 **/
  56. #endif
  57.  
  58.     rgppsiIns[128] = NULL; /** Make sure sample-info arrays are clear **/
  59.     for (i = 128; i--; ) {
  60.         rgpsiDrum[i] = NULL;
  61.         rgppsiIns[i] = NULL;
  62.     }
  63. }
  64.  
  65. void Error(Sz szMsg, ...)
  66. {
  67.     va_list args;
  68.  
  69.     fprintf(stderr, "%s: ", szProgram);
  70.     va_start(args, szMsg);
  71.     vfprintf(stderr, szMsg, args);
  72.     va_end(args);
  73.     fprintf(stderr, "\n");
  74. }
  75.  
  76. /*
  77.  * BuildFn: Builds a full filename given a string which is the old filename,
  78.  * and a default extension to use if one is not present in the string. After
  79.  * building the new filename, any extension in the old name is removed.
  80.  */
  81. void BuildFn(Fn fnNew, Sz fnOld, Sz fnExt)
  82. {
  83.     Sz fnT = fnNew;
  84.     int fExt = 0;
  85.  
  86.     while (*fnOld) {
  87.         if ('.' == (*(fnT++) = *fnOld)) { /** Copy a char, test for extension **/
  88.             fExt = 1;
  89.             *fnOld = 0; /** yes.. note extension exists and remove it **/
  90.         }
  91.         fnOld++;
  92.     }
  93.     if (!fExt) { /** If no extension found **/
  94.         *(fnT++) = '.';
  95.         while ((*(fnT++) = *(fnExt++))); /** copy the default one - fnExt **/
  96.     } else
  97.         *fnT = 0;
  98. }
  99.  
  100. /*
  101.  * SzReadPfile: Reads the next string from the textfile given and returns it.
  102.  * If file is at end, returns NULL.
  103.  */
  104. Sz SzReadPfile(FILE *pfileText)
  105. {
  106.     int ch, cch = 0, cchMac = 80;
  107.     Sz szT, szStart;
  108.  
  109.     if (feof(pfileText))
  110.         return NULL;
  111.     szStart = szT = (Sz) malloc(80); /** Set aside 80 characters for line **/
  112.     while ((ch = getc(pfileText)) != EOF && ch != '\n') {
  113.         *szT = ch;
  114.         if (++cch == cchMac) { /** If that's not enough **/
  115.             cchMac += 40;
  116.             szStart = (Sz) realloc(szStart, cchMac); /** increase in steps of 40 **/
  117.             szT = szStart + cch;
  118.         } else
  119.             szT++;
  120.     }
  121.     *szT = 0;
  122.     return (Sz) realloc(szStart, cch + 1);
  123. }
  124.  
  125. /*
  126.  * MakePsi: Given a filename and a place to store the new sample info,
  127.  * will set up the defaults for it.
  128.  *
  129.  * date: 30/6/1994
  130.  */
  131. void MakePsi(SI *psi, Sz fnSample)
  132. {
  133.   psi->fnSample = strdup(fnSample);
  134.   psi->pitch = -1;
  135.   psi->perpitch = MIDDLEC;
  136.   psi->freq = FreqGetFn(fnSample);
  137.   psi->sample = -1;
  138.   psi->bFinetune = 0;
  139.   psi->psiL = psi->psiR = NULL;
  140. }
  141.  
  142. /*
  143.  * PsiAddsample: Given a sample's filename, will look it up in the tree
  144.  * and return a pointer to it if it exists, else will create it and return
  145.  * a pointer to the newly created entry.
  146.  */
  147. SI *PsiAddsample(Sz fnSample)
  148. {
  149.     SI *psiT;
  150.  
  151.     if (NULL == psiTree) { /** If nothing in tree **/
  152.         psiT = psiTree = (SI *) malloc(sizeof(SI)); /** create root node **/
  153.     MakePsi(psiT, fnSample);
  154.     } else { /** Else **/
  155.         SI *psiOld;
  156.         int cmp;
  157.  
  158.         psiT = psiTree;
  159.         while (psiT != NULL) { /** find spot for sample in tree **/
  160.             psiOld = psiT;
  161.             cmp = strcmp(psiT->fnSample, fnSample);
  162.             if (!cmp)
  163.                 break;
  164.             else if (0 > cmp)
  165.                 psiT = psiT->psiL;
  166.             else
  167.                 psiT = psiT->psiR;
  168.         }
  169.         if (NULL == psiT) {
  170.             psiT = (SI *) malloc(sizeof(SI)); /** and create entry **/
  171.             if (0 > cmp)
  172.                 psiOld->psiL = psiT;
  173.             else
  174.                 psiOld->psiR = psiT;
  175.       MakePsi(psiT, fnSample);
  176.         }
  177.     }
  178.     return psiT;
  179. }
  180.  
  181. /*
  182.  * PsiPrunePsi: Returns the given sample tree, but without any redundant
  183.  * samples. Any redundant samples are freed. If no samples remain, NULL
  184.  * is returned.
  185.  */
  186. SI *PsiPrunePsi(SI *psi)
  187. {
  188.     if (NULL == psi)
  189.         return NULL;
  190.     psi->psiL = PsiPrunePsi(psi->psiL); /** Prune left and right branches **/
  191.     psi->psiR = PsiPrunePsi(psi->psiR);
  192.     if (-1 == psi->pitch) { /** If root of tree redundant, need to remove it **/
  193.         SI *psiT;
  194.  
  195.         if (NULL == psi->psiL) { /** If no left branch **/
  196.             psiT = psi->psiR;
  197.             free(psi); /** replace root **/
  198.             psi = psiT; /** with right branch **/
  199.         } else if (NULL == psi->psiR) { /** If no right branch **/
  200.             psiT = psi->psiL;
  201.             free(psi); /** replace root **/
  202.             psi = psiT; /** with left branch **/
  203.         } else if (NULL == psi->psiL->psiR) { /** If left branch has no right **/
  204.             psiT = psi->psiL;
  205.             psiT->psiR = psi->psiR; /** put right branch on right of left **/
  206.             free(psi); /** and replace root **/
  207.             psi = psiT; /** with left branch **/
  208.         } else { /** Else.. there's 2 full branches - yuck! **/
  209.             SI *psiOld;
  210.  
  211.             psiT = psi->psiL;
  212.             while (NULL != psiT->psiR) { /** Find rightmost entry on left branch **/
  213.                 psiOld = psiT;
  214.                 psiT = psiT->psiR;
  215.             }
  216.             psiOld->psiR = psiT->psiL;
  217.             psiT->psiL = psi->psiL;
  218.             psiT->psiR = psi->psiR;
  219.             free(psi); /** remove root **/
  220.             psi = psiT; /** and replace it with that entry **/
  221.         }
  222.     }
  223.     return psi;
  224. }
  225.  
  226. /*
  227.  * PitchConv: The supplied token is converted to the equivalent MIDI
  228.  * pitch value, eg. C2 = 60, C#2 = 61. -1 is returned if not a note.
  229.  *
  230.  * date: 30/6/1994
  231.  */
  232. int PitchConv(Sz szTok)
  233. {
  234.   static int rgbPitch[7] = {45, 47, 36, 38, 40, 41, 43};
  235.   int irgb;
  236.  
  237.   if ((irgb = toupper(szTok[0]) - 'A') < 0 || 6 < irgb)
  238.     return -1;
  239.   else if ('#' == szTok[1])
  240.     szTok++;
  241.   return rgbPitch[irgb] + 12 * atoi(szTok + 1) +
  242.     ('#' == szTok[0] ? 1 : 0);
  243. }
  244.  
  245. /*
  246.  * ReadconfigFn: Given the filename of the configuration file, it interprets
  247.  * each line and sets up options and sample-tables.
  248.  *
  249.  * date: 30/6/1994 - added ditto, sample paths, sample frequencies
  250.  *       1/7/1994 - added finetuning for those odd frequencies
  251.  */
  252. void ReadconfigFn(Sz fnConfig)
  253. {
  254.     FILE *pfileConfig;
  255.     Sz szLine, szTok;
  256.   int csz = 0, fError = 0, irgppsiLast = 0;
  257.   static Sz rgszIds[] = {
  258.     "M.K.",
  259.     "TDZ1", "TDZ2", "TDZ3", "M!K!", "5CHN", "6CHN", "7CHN", "8CHN",
  260.     "9CHN", "10CH", "11CH", "12CH", "13CH", "14CH", "15CH", "16CH",
  261.     "17CH", "18CH", "19CH", "20CH", "21CH", "22CH", "23CH", "24CH",
  262.     "25CH", "26CH", "27CH", "28CH", "29CH", "30CH", "31CH", "32CH"
  263.   };
  264.  
  265.     if (NULL == (pfileConfig = fopen(fnConfig, "rt"))) {
  266.         Error("Cannot find config file: %s", fnConfig);
  267.         exit(1);
  268.     }
  269.     while ((szLine = SzReadPfile(pfileConfig)) != NULL) { /** With every line.. **/
  270.         csz++;
  271.     if ('#' != szLine[0] && NULL != (szTok = strtok(szLine, WTSP)))
  272.             if ('0' <= szTok[0] && '9' >= szTok[0] || !strcmp(szTok, "def")) {
  273.                 int irgppsi, cpsi; /** If an instrument definition **/
  274.                 SI **ppsi;
  275.  
  276.                 if ('d' == szTok[0])
  277.                     irgppsi = 128;
  278.                 else
  279.                     irgppsi = atoi(szTok); /** decode instrument **/
  280.         if (irgppsi < 129) {
  281.           szTok = strtok(NULL, WTSP);
  282.           if (!strcmp(szTok, "\"")) /** Check for ditto **/
  283.             if (NULL == rgppsiIns[irgppsi])
  284.               rgppsiIns[irgppsi] = rgppsiIns[irgppsiLast]; /** Yes.. copy **/
  285.             else
  286.               fError = 1;
  287.           else { /** No.. **/
  288.             irgppsiLast = irgppsi;
  289.             while (NULL != szTok) { /*** With every sample.. ***/
  290.               if (NULL == rgppsiIns[irgppsi]) /*** Ensure allocated ***/
  291.                 rgppsiIns[irgppsi] = ppsi = (SI **) malloc(sizeof(SI *) * 2);
  292.               else {
  293.                 ppsi = rgppsiIns[irgppsi];
  294.